﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Fantom.Expression;
using System.Collections.ObjectModel;
using PPT = Microsoft.Office.Interop.PowerPoint;
using System.Collections.Specialized;

namespace An_Im.Expression.Model
{
    public class MainEditModel : INotifyPropertyChanged
    {


        #region prop


        /// <summary>
        /// 表达式模板列表。
        /// </summary>
        public SavedKeyPointFormulaList KeyPointFormulaList { get; set; } = SavedKeyPointFormulaList.DefaultList;

        /// <summary>
        /// 是否为最后一项。
        /// </summary>
        public bool IsNotLastItem => _isNotLastItem;
        private bool _isNotLastItem = false;

        /// <summary>
        /// 是否具有成员。
        /// </summary>
        public bool HasItem => _keyPointList.Count != 0;

        /// <summary>
        /// 选中的图形对象。
        /// </summary>
        /// 
        public KeyPointInformation SelectedItem
        {
            get => _selectedItem;
            set
            {
                _selectedItem = value;
                LoadUI(value);
            }
        }
        private KeyPointInformation _selectedItem;



        /// <summary>
        /// 选中的下标 ID
        /// </summary>
        public int SelectedIndex
        {
            get { return _keyPointList.IndexOf(_selectedItem); }
            set
            {
                var len = _keyPointList.Count;
                if (value >= len || value < 0)
                {
                    // ClearUIControl();
                    throw new Exception($"下标越界，请输入从 {0} 到 {len - 1} 的下标。");
                    return;
                }

                SelectedItem = _keyPointList[value];


            }
        }


        /// <summary>
        /// 关键帧列表。
        /// </summary>
        public ObservableCollection<KeyPointInformation> KeyPointList
        {
            get { return _keyPointList; }

        }
        private ObservableCollection<KeyPointInformation> _keyPointList;


        /// <summary>
        /// 动画属性类型
        /// </summary>
        public PPT.MsoAnimProperty AnimProperty
        {
            get { return _animProperty; }
            set
            {
                _animProperty = value;
                RaisePropertyChangedEvent();
            }
        }
        private PPT.MsoAnimProperty _animProperty;

        /// <summary>
        /// 错误信息。
        /// </summary>
        public string FormulaErrorInformation
        {
            get
            {
                return _errorInformation;
            }
            set
            {
                _errorInformation = value;
                RaisePropertyChangedEvent();
            }
        }
        private string _errorInformation = string.Empty;

        /// <summary>
        /// 起点错误信息。
        /// </summary>
        public string StartErrorInformation
        {
            get
            {
                return _startErrorInformation;
            }
            set
            {
                _startErrorInformation = value;
                RaisePropertyChangedEvent();
            }
        }
        private string _startErrorInformation = string.Empty;


        /// <summary>
        /// 终点错误信息。
        /// </summary>
        public string EndErrorInformation
        {
            get
            {
                return _endErrorInformation;
            }
            set
            {
                _endErrorInformation = value;
                RaisePropertyChangedEvent();
            }
        }
        private string _endErrorInformation = string.Empty;

        /// <summary>
        /// 当前表达式。
        /// </summary>
        public string CurrentFormula
        {
            get
            {
                return _selectedItem.Formula;
            }
            set
            {
                _selectedItem.Formula = value;
                RaisePropertyChangedEvent();
            }
        }

        /// <summary>
        /// 开始值。
        /// </summary>
        public string StartValue
        {
            get
            {
                return _selectedItem.Value;
            }
            set
            {
                _selectedItem.Value = value;
                RaisePropertyChangedEvent();
            }
        }

        /// <summary>
        /// 结束值。
        /// </summary>
        public string EndValue
        {
            get
            {
                TryGetNextKeyPointDefault(_selectedItem, out var nextItem);
                return nextItem.Value;
            }
            set
            {
                TryGetNextKeyPointDefault(_selectedItem, out var nextItem);
                nextItem.Value = value;
                RaisePropertyChangedEvent();
            }
        }

        /// <summary>
        /// 开始时间。
        /// </summary>
        public double StartTime
        {
            get
            {
                return _selectedItem.Time;
            }
            set
            {
                _selectedItem.Time = Math.Round(value, 4);
                RaisePropertyChangedEvent();
            }
        }

        /// <summary>
        /// 结束时间。
        /// </summary>
        public double EndTime
        {
            get
            {
                TryGetNextKeyPointDefault(_selectedItem, out var nextItem);
                return nextItem.Time;
            }
            set
            {
                TryGetNextKeyPointDefault(_selectedItem, out var nextItem);
                nextItem.Time = Math.Round(value, 4);
                RaisePropertyChangedEvent();
            }
        }

        /// <summary>
        /// 属性类型。
        /// </summary>
        public double PropertyType
        {
            get
            {
                return _propertyType;
            }
            set
            {
                if (_keyPointList.Count == 0) return;
                _propertyType = value;
                RaisePropertyChangedEvent();
            }
        }
        private double _propertyType;

        #endregion


        #region ctor

        public MainEditModel(PPT.MsoAnimProperty animProperty, IEnumerable<KeyPointInformation> keyPointInformationList)
        {
            _keyPointList = new ObservableCollection<KeyPointInformation>(keyPointInformationList);
            _animProperty = animProperty;
            _selectedItem = _keyPointList.First();
        }


        #endregion

        #region event
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChangedEvent([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        #endregion




        #region private

        /// <summary>
        /// 载入当前 UI。
        /// </summary>
        /// <param name="keyPoint"></param>
        private void LoadUI(KeyPointInformation keyPoint)
        {

            if (keyPoint == null)
            {
                _selectedItem = KeyPointInformation.DefaultFirst;
                StartTime = 0;
                StartValue = "0";
                EndTime = 1;
                EndValue = "0";
                CurrentFormula = "$";
                _isNotLastItem = HasItem;
            }
            else
            {
                var keypoint1 = keyPoint;
                var keypoint2 = KeyPointInformation.DefaultFirst;

                _isNotLastItem = TryGetNextKeyPoint(keypoint1, out keypoint2) && HasItem;


                StartTime = keypoint1.Time;
                StartValue = keypoint1.GetValue();

                if (_isNotLastItem) // 如果还存在下一个节点。
                {

                    EndTime = keypoint2.Time;
                    EndValue = keypoint2.GetValue();
                    CurrentFormula = keypoint1.Formula;
                }

            }


            RaisePropertyChangedEvent("IsNotLastItem");
            RaisePropertyChangedEvent("HasItem");
        }

        #region next and preview


        /// <summary>
        /// 尝试获得下一个节点。
        /// </summary>
        private bool TryGetNextKeyPoint(KeyPointInformation keyPoint, out KeyPointInformation nextPoint)
        {
            nextPoint = null;
            if (_keyPointList == null || _keyPointList.Count == 0 || _keyPointList.Count == 1) return false;

            var index = _keyPointList.IndexOf(keyPoint);

            if (index >= _keyPointList.Count - 1 || index == -1) return false;

            nextPoint = _keyPointList[index + 1];
            return true;
        }

        /// <summary>
        /// 尝试获得下一个节点。
        /// </summary>
        private bool TryGetNextKeyPointDefault(KeyPointInformation keyPoint, out KeyPointInformation nextPoint)
        {
            nextPoint = KeyPointInformation.DefaultLast;
            if (_keyPointList == null || _keyPointList.Count == 0 || _keyPointList.Count == 1) return false;

            var index = _keyPointList.IndexOf(keyPoint);

            if (index >= _keyPointList.Count - 1 || index == -1) return false;

            nextPoint = _keyPointList[index + 1];
            return true;
        }

        /// <summary>
        /// 获得下一个节点。
        /// </summary>
        private KeyPointInformation GetNextKeyPoint(KeyPointInformation keyPoint)
        {
            var index = _keyPointList.IndexOf(keyPoint);
            if (index >= _keyPointList.Count - 1 || index == -1)
                throw new Exception("指定节点之后无下一节点，已经是最后一个节点。");
            return _keyPointList[index];
        }


        /// <summary>
        /// 尝试获得下一个节点。
        /// </summary>
        private bool TryGetPreviewKeyPointDefault(KeyPointInformation keyPoint, out KeyPointInformation prevPoint)
        {
            prevPoint = KeyPointInformation.DefaultFirst;
            if (_keyPointList == null || _keyPointList.Count == 0 || _keyPointList.Count == 1) return false;

            var index = _keyPointList.IndexOf(keyPoint);

            if (index <= 0) return false;

            prevPoint = _keyPointList[index - 1];
            return true;
        }


        /// <summary>
        /// 尝试获得下一个节点。
        /// </summary>
        private bool TryGetPreviewKeyPoint(KeyPointInformation keyPoint, out KeyPointInformation prevPoint)
        {
            prevPoint = null;
            if (_keyPointList == null || _keyPointList.Count == 0 || _keyPointList.Count == 1) return false;

            var index = _keyPointList.IndexOf(keyPoint);

            if (index <= 0) return false;

            prevPoint = _keyPointList[index - 1];
            return true;
        }

        /// <summary>
        /// 查询之前的节点。
        /// </summary>
        /// <returns></returns>
        private KeyPointInformation GetPreviewKeyPoint(KeyPointInformation keyPoint)
        {
            var index = _keyPointList.IndexOf(keyPoint);
            if (index <= 0)
                throw new Exception("已经是最开始的一个节点。");
            return _keyPointList[index - 1];
        }


        #endregion



        #endregion


        #region public

        #region expression calculate



        /// <summary>
        /// 获得经过计算的表达式的错误文本
        /// </summary>
        /// <param name="metaNodes">原节点数组</param>
        /// <param name="errpos">错误节点</param>
        /// <param name="textErrorPos">文本中错误节点的位置，用于设置 TextBox 的光标位置</param>
        /// <returns></returns>
        private string GetErrorText(List<MetaNode> metaNodes, int errpos, out int textErrorPos)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < errpos; i++)
            {
                if (i != 0 && !metaNodes[i - 1].Type.HasFlag(MetaNodeType.Function))
                {
                    sb.Append(" ");
                }

                sb.Append(metaNodes[i].Text);
            }
            var templen = sb.Length;
            textErrorPos = templen + metaNodes[errpos].Text.Length + 1;

            sb.Append("【");
            sb.Append(metaNodes[errpos].Text);
            sb.Append("】");

            for (int i = errpos + 1; i < metaNodes.Count; i++)
            {
                if (!metaNodes[i - 1].Type.HasFlag(MetaNodeType.Function))
                {
                    sb.Append(" ");
                }
                sb.Append(metaNodes[i].Text);
            }
            return sb.ToString();
        }


        /// <summary>
        /// 计算表达式。
        /// </summary>
        /// <param name="innerCount">内置插帧的数量，默认为50</param>
        /// <returns></returns>
        public ExpressionResult CalculateExpression(KeyPointInformation keypoint1, KeyPointInformation keypoint2, double startValue, double endValue, int innerCount)
        {
            var rstdatalist = new List<double>();
            double startTime = keypoint1.Time, endTime = keypoint2.Time;
            var formula = keypoint1.Formula;
            var deltaTime = endTime - startTime;

            // 计算表达式。
            FormulaBuilder.CalculateExpression(
                exp: formula,
                    varValue: 0d,
                    functionDic: FunctionCatalog.DefaultFunctions,
                    systemConstDic: SystemConstCatalog.DefaultSystemConsts,
                    metaNodes: out var metaNodes,
                    root: out var root,
                    result: out var rst,
                    errPos: out var errpos);

            var strlist = new List<string>(from node in metaNodes select node.Text);

            var adjustedFormulaText = FormulaBuilder.GetAdjustedFormula(metaNodes);
            keypoint1.Formula = adjustedFormulaText;

            var formulaErrorInfomation = string.Empty;
            if (errpos > -1)
                formulaErrorInfomation = GetErrorText(metaNodes, errpos, out var formulaErrTextPos);


            if (errpos == -1)
            {
                rstdatalist = new List<double>();

                var deltaValue = endValue - startValue;

                int calcLen = innerCount;
                var tempnum = Math.Ceiling(Math.Abs(deltaValue));
                if (tempnum > 1d)
                    calcLen = (int)tempnum * innerCount;
                for (int i = 0; i <= calcLen; i++)
                {
                    rstdatalist.Add(Math.Round(FormulaBuilder.CalcExpressionTree(root, FunctionCatalog.DefaultFunctions, SystemConstCatalog.DefaultSystemConsts, startValue + (1d * i / calcLen) * deltaValue), 8));
                }

            }

            return new ExpressionResult(rstdatalist, deltaTime > 0, true, true, errpos == -1, string.Empty, string.Empty, formulaErrorInfomation);

        }

        /// <summary>
        /// 计算表达式。
        /// </summary>
        /// <param name="innerCount">内置插帧的数量，默认为50</param>
        /// <returns></returns>
        public ExpressionResult CalculateExpression(KeyPointInformation keypoint1, KeyPointInformation keypoint2, int innerCount)
        {
            var rstdatalist = new List<double>();
            var curItem = keypoint1;
            var nextItem = keypoint2;
            var formula = curItem.Formula;
            var startTime = curItem.Time;
            var startValueText = curItem.GetValue();
            var endTime = nextItem.Time;
            var endValueText = nextItem.GetValue();

            var deltaTime = endTime - startTime;


            // 获得起始值。
            FormulaBuilder.CalculateExpression(
               exp: startValueText,
                   varValue: 0d,
                   functionDic: FunctionCatalog.DefaultFunctions,
                   systemConstDic: SystemConstCatalog.DefaultSystemConsts,
                   metaNodes: out List<MetaNode> startValueMetaNodes,
                   root: out MetaNode startValueroot,
                   result: out double startValue,
                   errPos: out int startValueErrPos);

            // 获得结束值。
            FormulaBuilder.CalculateExpression(
             exp: endValueText,
                 varValue: 0d,
                 functionDic: FunctionCatalog.DefaultFunctions,
                 systemConstDic: SystemConstCatalog.DefaultSystemConsts,
                 metaNodes: out List<MetaNode> endValueMetaNodes,
                 root: out MetaNode endValueroot,
                 result: out double endValue,
                 errPos: out int endValueErrPos);

            // 计算表达式。
            FormulaBuilder.CalculateExpression(
                exp: formula,
                    varValue: 0d,
                    functionDic: FunctionCatalog.DefaultFunctions,
                    systemConstDic: SystemConstCatalog.DefaultSystemConsts,
                    metaNodes: out var metaNodes,
                    root: out var root,
                    result: out var rst,
                    errPos: out var errpos);

            var strlist = new List<string>(from node in metaNodes select node.Text);

            // 调整后的文本。
            var adjustedStartValueText = FormulaBuilder.GetAdjustedFormula(startValueMetaNodes);
            curItem.SetValue(adjustedStartValueText);

            var adjustedEndValueText = FormulaBuilder.GetAdjustedFormula(endValueMetaNodes);
            nextItem.SetValue(adjustedEndValueText);

            var adjustedFormulaText = FormulaBuilder.GetAdjustedFormula(metaNodes);
            curItem.Formula = adjustedFormulaText;

            var startErrorInfomation = string.Empty;
            var endErrorInfomation = string.Empty;
            var formulaErrorInfomation = string.Empty;

            if (startValueErrPos > -1)
                startErrorInfomation = GetErrorText(startValueMetaNodes, startValueErrPos, out var startValueErrTextPos);

            if (endValueErrPos > -1)
                endErrorInfomation = GetErrorText(endValueMetaNodes, endValueErrPos, out var endValueErrTextPos);

            if (errpos > -1)
                formulaErrorInfomation = GetErrorText(metaNodes, errpos, out var formulaErrTextPos);


            if (startValueErrPos == -1 && endValueErrPos == -1 && errpos == -1)
            {
                rstdatalist = new List<double>();

                var deltaValue = endValue - startValue;

                int calcLen = innerCount;
                var tempnum = Math.Ceiling(Math.Abs(deltaValue));
                if (tempnum > 1d)
                    calcLen = (int)tempnum * innerCount;
                for (int i = 0; i <= calcLen; i++)
                {
                    rstdatalist.Add(Math.Round(FormulaBuilder.CalcExpressionTree(root, FunctionCatalog.DefaultFunctions, SystemConstCatalog.DefaultSystemConsts, startValue + (1d * i / calcLen) * deltaValue), 8));
                }

            }

            return new ExpressionResult(rstdatalist,
                deltaTime > 0,
                startValueErrPos == -1,
                endValueErrPos == -1,
                errpos == -1,
                startErrorInfomation,
                endErrorInfomation,
                formulaErrorInfomation);

        }

        public ExpressionResult CalculateExpression(int innerCount)
        {
            TryGetNextKeyPointDefault(_selectedItem, out var nextItem);
            return CalculateExpression(_selectedItem, nextItem, innerCount);
        }

        public ExpressionResult CalculateExpression()
        {
            return CalculateExpression(50);
        }

        public ExpressionListResult CalculateAllExpression(IList<KeyPointInformation> keyPointList, int innerCount)
        {
            var expressionListResult = new ExpressionListResult();

            var valueArr = new double[keyPointList.Count];
            var valueValidArr = new bool[keyPointList.Count];
            var valueErrorStringArr = new string[keyPointList.Count];
            var adjValueStringArr = new string[keyPointList.Count];

            // 先将值的内容全部计算得到
            for (int i = 0; i < keyPointList.Count; i++)
            {
                var valueText = keyPointList[i].GetValue();
                FormulaBuilder.CalculateExpression(
                  exp: valueText,
                  varValue: 0d,
                  functionDic: FunctionCatalog.DefaultFunctions,
                  systemConstDic: SystemConstCatalog.DefaultSystemConsts,
                  metaNodes: out List<MetaNode> valueMetaNodes,
                  root: out MetaNode valueroot,
                  result: out double resultValue,
                  errPos: out int valueErrPos);

                valueArr[i] = resultValue;
                valueValidArr[i] = valueErrPos == -1;

                if (valueErrPos > -1)
                    valueErrorStringArr[i] = GetErrorText(valueMetaNodes, valueErrPos, out var valueErrTextPos);

                adjValueStringArr[i] = FormulaBuilder.GetAdjustedFormula(valueMetaNodes);
                keyPointList[i].SetValue(adjValueStringArr[i]);
            }

            for (int i = 0; i < keyPointList.Count - 1; i++)
            {
                var curItem = keyPointList[i];
                var nextItem = keyPointList[i + 1];
                ExpressionResult result = null;
                if (valueValidArr[i] && valueValidArr[i + 1])
                    result = CalculateExpression(curItem, nextItem, valueArr[i], valueArr[i + 1], innerCount);
                else
                    result = new ExpressionResult(null, true, valueValidArr[i], valueValidArr[i + 1], true, valueErrorStringArr[i], valueErrorStringArr[i + 1], string.Empty);
                expressionListResult.Results.Add(result);
            }

            StartErrorInformation = expressionListResult.GetFirstErrorStartValueString();
            EndErrorInformation = expressionListResult.GetFirstEndErrorValueString();
            FormulaErrorInformation = expressionListResult.GetFirstFormulaString();

            RaisePropertyChangedEvent("StartValue");
            RaisePropertyChangedEvent("EndValue");
            RaisePropertyChangedEvent("CurrentFormula");

            return expressionListResult;
        }


        #endregion  expression calculate


        /// <summary>
        /// 在选中点之后添加节点。
        /// </summary>
        /// <returns></returns>
        public void AddAfterItem(KeyPointInformation keyPointInformation)
        {
            if (_keyPointList.Count == 0)
            {
                SelectedItem = AddItemInEmptyList();
            }
            else
            {
                TryGetNextKeyPointDefault(keyPointInformation, out var item2);
                _keyPointList.Insert(SelectedIndex + 1, new KeyPointInformation((keyPointInformation.Time + item2.Time) / 2, "0", "$"));
            }

        }

        /// <summary>
        /// 在选中点之后添加顶点。
        /// </summary>
        /// <returns></returns>
        public void AddBeforeItem(KeyPointInformation keyPointInformation)
        {
            if (_keyPointList.Count == 0)
            {
                SelectedItem = AddItemInEmptyList();
            }
            else
            {
                TryGetPreviewKeyPointDefault(keyPointInformation, out var item1);
                _keyPointList.Insert(SelectedIndex + 1, new KeyPointInformation((item1.Time + keyPointInformation.Time) / 2, "0", "$"));
            }

        }

        /// <summary>
        /// 添加收尾顶点。
        /// </summary>
        public KeyPointInformation AddItemInEmptyList()
        {
            var first = new KeyPointInformation(KeyPointInformation.DefaultFirst);
            _keyPointList.Add(first);
            _keyPointList.Add(new KeyPointInformation(KeyPointInformation.DefaultLast));
            return first;
        }




        /// <summary>
        /// 移除节点。
        /// </summary>
        public void RemoveItem(KeyPointInformation keyPointInformation)
        {
            TryGetPreviewKeyPoint(keyPointInformation, out var nextp);
            TryGetNextKeyPoint(keyPointInformation, out var nextn);

            if (nextn != null)
            {
                SelectedItem = nextn;
            }
            else
            {
                SelectedItem = nextp;
            }


            _keyPointList.Remove(keyPointInformation);

            LoadUI(_selectedItem);
        }



        #endregion



    }
}
